package org.mydba.report.gui;

import static org.mydba.report.gui.MyDBAReport.FRAME_WIDTH;
import org.mydba.util.DBAHelper;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MiniMapPanel extends JPanel {

    private static Color topMountain = Color.RED;
    private static Color bottomMountain = Color.GREEN;

    private MyDBAReport report;
    private SessionsPanel sessionsPanel;

    private BufferedImage image = new BufferedImage(FRAME_WIDTH, 80, BufferedImage.TYPE_INT_RGB);

    private int squareX;
    private int squareWidth;
    private int squareWidthDiv2;

    MiniMapPanel(MyDBAReport report, Connection c, SessionsPanel sessionsPanel) {
        this.report = report;
        this.sessionsPanel = sessionsPanel;
        setBackground(Color.WHITE);
        createMiniMap(report, c);

        // add motion listener (updated with each move)
        addMouseMotionListener(new MouseAdapter() {
            public void mouseMoved(MouseEvent e) {
                moveSquare(e.getX());
            }

            public void mouseEntered(MouseEvent e) {
                moveSquare(e.getX());
            }
        });
    }

    public synchronized int getSquareX() {
        return squareX;
    }

    private synchronized void moveSquare(int x) {
        //System.out.println("X: " + x + ", squareX: " + squareX);

        if (x != squareX) {
            if (x > FRAME_WIDTH - squareWidthDiv2) {
                if( squareX == FRAME_WIDTH - squareWidthDiv2) {
                    return;
                }

                x = FRAME_WIDTH-squareWidthDiv2;
            }

            if (x < squareWidthDiv2 ) {
                if( squareX == 0 ) {
                    return;
                }

                x = squareWidthDiv2;
            }

            squareX = x - squareWidthDiv2;
            sessionsPanel.setSquareX(squareX);

            repaint();
            sessionsPanel.repaint();
        }
    }

    private void createMiniMap(MyDBAReport report, Connection c) {

        PreparedStatement p = null;
        ResultSet r;

        try {
            final Graphics2D g2d = image.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);

            // fill background color as white
            g2d.setColor(Color.WHITE);
            g2d.fill(new Rectangle2D.Float(0, 0, FRAME_WIDTH, 80));

            final double start = (double) report.getStartTimeNano();
            final double end = (double) report.getStopTimeNano();
            final double step = ((double) (report.getStopTimeNano() - report.getStartTimeNano())) / (double) FRAME_WIDTH;

            // retrieve number of sessions running at time T to fill the 800 pixels of the picture 
            p = c.prepareStatement("select count(distinct sid) " +
                                   "  from sqlexec " +
                                   " where " +
                                   // query execution
                                   "       ? between stime and etime" +
                                   "    or (stime between ? and ? and etime between ? and ?)" +
                                   "    or ? between stime and etime" +
                                   // query fetch
                                   "    or ? between rset_next_start_time_ns and rset_next_start_time_ns+rset_next_duration" +
                                   "    or (rset_next_start_time_ns between ? and ? and rset_next_start_time_ns+rset_next_duration between ? and ?)" +
                                   "    or ? between rset_next_start_time_ns and rset_next_start_time_ns+rset_next_duration" +
                                   // update
                                   "    or ? between rows_update_start_time_ns and rows_update_start_time_ns+rows_update_duration" +
                                   "    or (rows_update_start_time_ns between ? and ? and rows_update_start_time_ns+rows_update_duration between ? and ?)" +
                                   "    or ? between rows_update_start_time_ns and rows_update_start_time_ns+rows_update_duration"
            );

            final int[] xPoints = new int[FRAME_WIDTH + 2];
            final int[] yPoints = new int[FRAME_WIDTH + 2];

            int maxSessions = 0;
            int i = 0;
            for (double t = start; t < end; t += step, i++) {
                final long tMin = (long) t;
                final long tMax = (long) (t + step);
                p.setLong(1, tMin);
                p.setLong(2, tMin);
                p.setLong(3, tMax);
                p.setLong(4, tMin);
                p.setLong(5, tMax);
                p.setLong(6, tMax);

                p.setLong(7, tMin);
                p.setLong(8, tMin);
                p.setLong(9, tMax);
                p.setLong(10, tMin);
                p.setLong(11, tMax);
                p.setLong(12, tMax);

                p.setLong(13, tMin);
                p.setLong(14, tMin);
                p.setLong(15, tMax);
                p.setLong(16, tMin);
                p.setLong(17, tMax);
                p.setLong(18, tMax);

                r = p.executeQuery();

                if (r.next()) {
                    xPoints[i] = i;
                    yPoints[i] = r.getInt(1);
                    maxSessions = Math.max(maxSessions, yPoints[i]);
                }

                DBAHelper.close(r);
            }

            final double yScale = 80.0d / (double) maxSessions;

            for (int j = 0; j < FRAME_WIDTH; j++) {
                yPoints[j] = 80 - (int) (yPoints[j] * yScale);
            }

            xPoints[FRAME_WIDTH] = xPoints[FRAME_WIDTH - 1];
            yPoints[FRAME_WIDTH] = 80;

            xPoints[FRAME_WIDTH + 1] = xPoints[0];
            yPoints[FRAME_WIDTH + 1] = 80;

            final Polygon polygon = new Polygon(xPoints, yPoints, xPoints.length);

            final GradientPaint yellowPaint = new GradientPaint(0.0F, 0.0F, topMountain, 0.0F, 80, bottomMountain);

            try {
                g2d.setColor(Color.GRAY);
                g2d.setStroke(new BasicStroke(2));
                g2d.drawPolyline(polygon.xpoints, polygon.ypoints, polygon.npoints);
            }
            catch (Exception ignored) {
            }

            g2d.setPaint(yellowPaint);
            g2d.fillPolygon(polygon);

            squareWidth = (int) Math.ceil((double) FRAME_WIDTH / (double) ((report.getStopTimeNano() - report.getStartTimeNano()) / 1000000000.0d));
            squareWidthDiv2 = (int)Math.ceil(squareWidth / 2.0d);

            //System.out.println("squareWidth = "+squareWidth);
            //System.out.println("squareWidthDiv2 = "+squareWidthDiv2);

            sessionsPanel.setSquareWidth(squareWidth);
        }
        catch (SQLException sqle) {
            sqle.printStackTrace();
        }
        finally {
            DBAHelper.close(p);
        }
    }

    public Dimension getPreferredSize() {
        return new Dimension(FRAME_WIDTH, 84);
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 2, null);
        g.setColor(Color.RED);
        g.drawRect(squareX, 0, squareWidth, 83);
    }
}